Hexo 解决 hexo-bilibili-bangumi 插件命令冲突教程

Hexo 解决 hexo-bilibili-bangumi 插件命令冲突教程

一、问题背景

使用 Hexo 博客的 hexo-bilibili-bangumi 插件时,其默认注册的 bangumi「cinema」「game」等命令可能与 Hexo 核心命令(如 hexo g「hexo d)或其他插件命令冲突,导致执行 hexo g -d` 等核心命令时输出帮助信息,无法正常生成或部署博客。

二、解决思路

通过修改插件源码,为自定义命令添加唯一前缀(如 bilibili-),使插件命令变为 bilibili-bangumi「bilibili-cinema「bilibili-game,彻底避免与核心命令冲突,同时保留插件原有功能。

三、前置准备

  1. 已安装 Node.js 和 npm(Hexo 运行基础环境);
  2. 已搭建 Hexo 博客项目,且知道项目根目录路径;
  3. 已卸载冲突插件(若之前安装过):
    1
    npm uninstall hexo-bilibili-bangumi --save

四、完整解决步骤

步骤 1:重新安装插件

在 Hexo 博客根目录打开命令行,执行安装命令:

1
npm install hexo-bilibili-bangumi --save

安装完成后,插件文件会存储在 node_modules/hexo-bilibili-bangumi/ 目录下。

步骤 2:配置插件基础参数

打开 Hexo 根目录的 _config.yml 文件,添加插件配置(必填项确保功能可用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# bilibili 插件配置
bangumi:
enable: true # 启用番剧功能
vmid: 你的B站UID # 替换为B站个人主页地址栏的纯数字ID
source: bilibili # 数据源选择bilibili
title: 我的追番列表 # 自定义番剧页面标题(可选)
cinema:
enable: true # 启用影视功能
vmid: 你的B站UID # 与番剧功能共用同一个UID
source: bilibili # 数据源选择bilibili
title: 我的影视清单 # 自定义影视页面标题(可选)
game:
enable: true # 启用游戏功能
vmid: 你的B站UID # 与番剧功能共用同一个UID
source: bgmv0 # 游戏功能仅支持bgmv0数据源
title: 我的游戏收藏 # 自定义游戏页面标题(可选)

步骤 3:修改插件源码(核心步骤)

3.1 找到插件核心文件

进入插件安装目录,定位到命令注册的核心文件:

1
你的Hexo根目录/node_modules/hexo-bilibili-bangumi/src/index.js

3.2 替换修改后的代码

用文本编辑器(如 VS Code、记事本)打开 index.js 文件,删除原有内容,粘贴以下修改后的完整代码(仅修改命令注册部分,其他功能不变):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
/*
* @Author : HCLonely
* @Date : 2024-09-11 15:40:57
* @LastEditTime : 2025-07-10 14:01:12
* @LastEditors : HCLonely
* @FilePath : /hexo-bilibili-bangumi/src/index.js
* @Description : Hexo插件主入口文件,提供了bangumi、cinema和game三个命令,
* 用于生成和管理番剧、影视和游戏页面。支持从多个数据源获取数据,
* 包括bilibili、bangumi.tv和AniList等,并提供数据更新和删除功能。
*/

var fs = require('hexo-fs');
var path = require('path');
var hexoLog = require('hexo-log');

/**
* @constant {Object} log - 统一的日志记录器实例
* @description 配置了统一的日志输出工具
*/
var log = (typeof hexoLog["default"] === 'function' ? hexoLog["default"] : hexoLog)({
debug: false,
silent: false
});
var _require = require('./lib/get-bili-data'),
getBiliData = _require.getBiliData;
var _require2 = require('./lib/get-bgm-data'),
getBgmData = _require2.getBgmData;
var _require3 = require('./lib/get-bgmv0-data'),
getBgmv0Data = _require3.getBgmv0Data;
var _require4 = require('./lib/get-anilist-data'),
getAnilistData = _require4.getAnilistData;
var _require5 = require('./lib/get-simkl-data'),
getSimklData = _require5.getSimklData;
if (typeof URL !== 'function') {
var _require6 = require('url'),
_URL = _require6.URL;
global.URL = _URL;
}

/**
* @constant {Object} COMMAND_OPTIONS - 命令行选项配置
* @property {Array<Object>} options - 可用的命令行选项列表
*/
var COMMAND_OPTIONS = {
options: [{
name: '-u, --update',
desc: 'Update data'
}, {
name: '-d, --delete',
desc: 'Delete data'
}]
};

/**
* @constant {Object} DATA_TYPES - 数据类型配置对象
* @property {Object} bangumi - 番剧相关配置
* @property {Object} cinema - 影视相关配置
* @property {Object} game - 游戏相关配置
*/
var DATA_TYPES = {
bangumi: {
jsonFile: 'bangumis.json',
configKey: 'bangumi'
},
cinema: {
jsonFile: 'cinemas.json',
configKey: 'cinema'
},
game: {
jsonFile: 'games.json',
configKey: 'game'
}
};

/**
* @description 注册页面生成器
* 为每种数据类型(bangumi、cinema、game)注册对应的页面生成器
*/
Object.entries(DATA_TYPES).forEach(function (_ref) {
var _ref2 = (0, _slicedToArray2["default"])(_ref, 2),
type = _ref2[0],
config = _ref2[1];
hexo.extend.generator.register("bangumis-".concat(type), function (locals) {
var _this$config;
if (!(this !== null && this !== void 0 && (_this$config = this.config) !== null && _this$config !== void 0 && (_this$config = _this$config[config.configKey]) !== null && _this$config !== void 0 && _this$config.enable)) {
return;
}
return require('./lib/bangumi-generator').call(this, locals, type);
});
});

/**
* @function validateConfig
* @param {Object} config - 配置对象
* @returns {boolean} 配置是否有效
* @description 验证配置对象是否包含必要的字段和值
*/
var validateConfig = function validateConfig(config) {
if (!config) {
log.info('Please add config to _config.yml');
return false;
}
if (!config.enable) {
return false;
}
if (!config.vmid) {
log.info('Please add vmid to _config.yml');
return false;
}
return true;
};

/**
* @function handleDataDelete
* @param {string} sourceDir - 源目录路径
* @param {string} type - 数据类型(bangumi/cinema/game)
* @description 处理数据删除操作,删除指定类型的JSON数据文件
*/
var handleDataDelete = function handleDataDelete(sourceDir, type) {
var jsonPath = path.join(sourceDir, "/_data/".concat(DATA_TYPES[type].jsonFile));
if (fs.existsSync(jsonPath)) {
fs.unlinkSync(jsonPath);
log.info("".concat(type, " data has been deleted"));
}
};

/**
* @function handleDataUpdate
* @async
* @param {Object} config - 更新配置
* @param {string} type - 数据类型
* @param {string} sourceDir - 源目录路径
* @param {Object} args - 命令行参数
* @returns {Promise} 更新操作的Promise
* @description 根据不同的数据源处理数据更新操作
*/
var handleDataUpdate = /*#__PURE__*/function () {
var _ref3 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(config, type, sourceDir, args) {
var _config$progress, _config$coverMirror;
var baseConfig, typeMapping, _t;
return _regenerator["default"].wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
baseConfig = {
vmid: config.vmid,
showProgress: (_config$progress = config.progress) !== null && _config$progress !== void 0 ? _config$progress : true,
sourceDir: sourceDir,
extraOrder: config.extraOrder,
pagination: config.pagination,
coverMirror: (_config$coverMirror = config.coverMirror) !== null && _config$coverMirror !== void 0 ? _config$coverMirror : ''
};
_t = config.source;
_context.next = _t === 'bgm' ? 1 : _t === 'bangumi' ? 1 : _t === 'bgmv0' ? 2 : _t === 'anilist' ? 3 : _t === 'simkl' ? 5 : 6;
break;
case 1:
return _context.abrupt("return", getBgmData(_objectSpread(_objectSpread({}, baseConfig), {}, {
type: type,
proxy: config.proxy,
infoApi: config.bgmInfoApi,
host: "".concat(config.source, ".tv")
})));
case 2:
typeMapping = {
bangumi: 2,
cinema: 6,
game: 4
};
return _context.abrupt("return", getBgmv0Data(_objectSpread(_objectSpread({}, baseConfig), {}, {
type: typeMapping[type],
proxy: config.proxy
})));
case 3:
if (!(type === 'bangumi')) {
_context.next = 4;
break;
}
return _context.abrupt("return", getAnilistData(_objectSpread(_objectSpread({}, baseConfig), {}, {
type: 'ANIME'
})));
case 4:
log.info("".concat(config.source, " not support for ").concat(type));
return _context.abrupt("return");
case 5:
return _context.abrupt("return", getSimklData(_objectSpread(_objectSpread({}, baseConfig), {}, {
type: type
})));
case 6:
return _context.abrupt("return", getBiliData(_objectSpread(_objectSpread({}, baseConfig), {}, {
type: type,
useWebp: config.webp,
SESSDATA: typeof args.u === 'string' ? args.u : null
})));
case 7:
case "end":
return _context.stop();
}
}, _callee);
}));
return function handleDataUpdate(_x, _x2, _x3, _x4) {
return _ref3.apply(this, arguments);
};
}();

/**
* @description 注册命令(已修改:添加bilibili-前缀避免冲突)
* 为每种数据类型注册对应的命令,支持更新(-u)和删除(-d)操作
*/
Object.entries(DATA_TYPES).forEach(function (_ref4) {
var _ref5 = (0, _slicedToArray2["default"])(_ref4, 2),
type = _ref5[0],
config = _ref5[1];
// 核心修改:命令名添加bilibili-前缀,变为bilibili-bangumi、bilibili-cinema、bilibili-game
hexo.extend.console.register(`bilibili-${type}`, "Generate pages of ".concat(type, " for Hexo"), COMMAND_OPTIONS, function (args) {
if (args.d) {
handleDataDelete(this.source_dir, type);
} else if (args.u) {
var typeConfig = this.config[config.configKey];
if (!validateConfig(typeConfig)) {
return;
}
if (type === 'game' && typeConfig.source !== 'bgmv0') {
log.info("".concat(typeConfig.source, " not support for game"));
return;
}
handleDataUpdate(typeConfig, type, this.source_dir, args);
} else {
// 优化提示:同步更新命令名称,方便用户查看
log.info("Unknown command, please use \"hexo bilibili-".concat(type, " -h\" to see the available commands"));
}
});
});

保存文件并关闭编辑器。

步骤 4:测试功能与冲突解决效果

按以下步骤验证插件功能正常,且核心命令无冲突:

4.1 验证新命令注册成功

在 Hexo 根目录执行命令,查看所有可用 Hexo 命令:

1
hexo --help

验证标准

  • 「Commands」部分出现 bilibili-bangumi「bilibili-cinema「bilibili-game 三个新命令;
  • Hexo 核心命令(generate (g)「deploy (d)「clean 等)正常显示。

4.2 测试插件数据更新功能

执行新命令更新番剧数据(影视/游戏同理):

1
2
3
4
# 清理缓存
hexo clean
# 更新番剧数据
hexo bilibili-bangumi -u

验证标准

  • 命令行无报错,出现「Fetching bangumi data…」「Updated successfully」提示;
  • 根目录 source/_data/ 文件夹下生成 bangumis.json 文件。

4.3 测试核心命令正常执行

执行生成+部署命令,确认无冲突:

1
hexo g -d

验证标准

  • 正常生成静态文件(输出「Generated in x ms」);
  • 自动部署到远程仓库(输出「Deploy done: git」);
  • 无帮助信息输出,无命令识别错误。

4.4 本地预览页面

启动本地服务器,验证插件页面可访问:

1
hexo s

打开浏览器访问 http://localhost:4000/bangumi/(番剧页面)、http://localhost:4000/cinema/(影视页面),验证标准:页面正常显示B站番剧/影视列表,无空白或报错。

五、插件常用命令(修改后)

功能 命令
更新番剧数据 hexo bilibili-bangumi -u
删除番剧数据 hexo bilibili-bangumi -d
更新影视数据 hexo bilibili-cinema -u
删除影视数据 hexo bilibili-cinema -d
更新游戏数据 hexo bilibili-game -u
删除游戏数据 hexo bilibili-game -d

六、常见问题排查

  1. 执行更新命令报错「VMID not found」:检查 _config.ymlvmid 配置是否为B站纯数字UID(个人主页地址栏获取);
  2. 生成后无插件页面:确认 _config.ymlenable 设为 true,且 source/_data/ 下已生成对应 JSON 数据文件;
  3. 本地预览404:检查插件配置中的 path 项(默认路径为 bangumi「cinema「game),访问时需对应路径;
  4. 核心命令仍冲突:重新检查 index.js 中命令注册部分是否改为 bilibili-${type},确保文件已保存。